<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="a6xRf" id="a6xRf"><span data-lake-id="ud8d0aa6a" id="ud8d0aa6a">典型回答</span></h1>
  <p data-lake-id="ud382d17b" id="ud382d17b"><span data-lake-id="u6bab397c" id="u6bab397c">现在很多网站的背后都是一个庞大的分布式系统，多个系统之间的交互大多数都是采用RPC的方式，但是因为是远程调用，所以被调用者的服务的可用情况其实是不可控的。</span></p>
  <p data-lake-id="uc393ca51" id="uc393ca51"><span data-lake-id="ue608d9fd" id="ue608d9fd">​</span><br></p>
  <p data-lake-id="u586b10f4" id="u586b10f4"><span data-lake-id="ud57e784f" id="ud57e784f">而越是庞大的系统，上下游的调用链就会越长，而如果在一个很长的调用链中，某一个服务由于某种原因导致响应时间很长，或者完全无响应，那么就可能把整个分布式系统都拖垮。</span></p>
  <p data-lake-id="ub513881f" id="ub513881f"><span data-lake-id="ubab44698" id="ubab44698">​</span><br></p>
  <p data-lake-id="u660183e6" id="u660183e6"><span data-lake-id="uc1dfa9e1" id="uc1dfa9e1">如果其中某一个服务由于自身原因导致响应很慢，那么就可能导致上游的服务相应也很慢，这样循环往复，就会导致整个系统全线崩溃，这就是</span><strong><span data-lake-id="u286f78c6" id="u286f78c6">服务雪崩</span></strong><span data-lake-id="u9ed6a562" id="u9ed6a562">。</span></p>
  <p data-lake-id="uc26ce852" id="uc26ce852"><span data-lake-id="ubde201d0" id="ubde201d0">​</span><br></p>
  <p data-lake-id="u8b6848b5" id="u8b6848b5"><span data-lake-id="u1f067bba" id="u1f067bba">在服务的依赖调用中，当被调用方出现故障时，出于自我保护的目的，调用方会主动停止调用，并根据业务需要进行相应处理。调用方这种主动停止调用的行为我们称之为熔断。</span></p>
  <p data-lake-id="u270dffa2" id="u270dffa2"><br></p>
  <h1 data-lake-id="r3rTB" id="r3rTB"><span data-lake-id="u3d45a714" id="u3d45a714">扩展知识</span></h1>
  <p data-lake-id="uf7ce088d" id="uf7ce088d"><br></p>
  <h3 data-lake-id="bec321c5" id="bec321c5"><span data-lake-id="u87b965de" id="u87b965de">为什么需要熔断</span></h3>
  <p data-lake-id="u06316b38" id="u06316b38"><br></p>
  <p data-lake-id="uada5d241" id="uada5d241"><span data-lake-id="u854cb701" id="u854cb701">其实，在分布式系统中，为了保证整体服务可用性和一致性，很多系统都会引入重试机制，在有些情况下，重试其实是可以解决问题的，比如网络问题等，都可以通过重试来解决。</span></p>
  <p data-lake-id="u6f542885" id="u6f542885"><br></p>
  <p data-lake-id="u0681711c" id="u0681711c"><span data-lake-id="uf8d0e0d5" id="uf8d0e0d5">但是，有些情况下，重试并不能解决问题，反而会加剧问题的严重性，比如下游系统因为请求量太大，导致CPU已经被打满，数据库连接池被占满，这时候上游系统调不通就会不断进行重试，这种重试请求，对于下游系统来说，无疑是雪上加霜，给下游系统造成二次伤害。</span></p>
  <p data-lake-id="u0b844ea5" id="u0b844ea5"><br></p>
  <p data-lake-id="u8012cdbe" id="u8012cdbe"><span data-lake-id="u3cf14330" id="u3cf14330">而分布式系统，大多数的服务雪崩也都是因为不断重试导致的，这种重试有可能是框架级别的自动重试、有可能是代码级别的重试逻辑、还有可能是用户的主动重试。</span></p>
  <p data-lake-id="u15f3395b" id="u15f3395b"><br></p>
  <p data-lake-id="u7c1bd5a5" id="u7c1bd5a5"><span data-lake-id="uee9e7c4d" id="uee9e7c4d">有些重试是无法避免的，而且如果因为防止雪崩，就不设计重试机制，也是一种因噎废食。</span></p>
  <p data-lake-id="u8fe24b83" id="u8fe24b83"><br></p>
  <h3 data-lake-id="f9dd477e" id="f9dd477e"><span data-lake-id="ue5952b54" id="ue5952b54">熔断器模式</span></h3>
  <p data-lake-id="u6b918b42" id="u6b918b42"><br></p>
  <p data-lake-id="uee36fd59" id="uee36fd59"><span data-lake-id="u8c740bf1" id="u8c740bf1">熔断器模式（Circuit Breaker Pattern），是一个现代软件开发的设计模式。用以侦测错误，并避免不断地触发相同的错误（如维护时服务不可用、暂时性的系统问题或是未知的系统错误）。</span></p>
  <p data-lake-id="u5cfdeda2" id="u5cfdeda2"><br></p>
  <p data-lake-id="u52026246" id="u52026246"><span data-lake-id="ue29f5929" id="ue29f5929">假设有个应用程序每秒会与数据库沟通数百次，此时数据库突然发生了错误，程序员并不会希望在错误时还不断地访问数据库。因此会想办法直接处理这个错误，并进入正常的结束程序。简单来说，</span></p>
  <p data-lake-id="u6f37e2f1" id="u6f37e2f1"><br></p>
  <p data-lake-id="u25e6fd85" id="u25e6fd85"><span data-lake-id="u6ce618e5" id="u6ce618e5">熔断器会侦测错误并且“预防”应用程序不断地重试调用一个近乎毫无回应的服务（除非该服务已经安全到可重试连线了）。</span></p>
  <p data-lake-id="u48bcf687" id="u48bcf687"><br></p>
  <p data-lake-id="u4f4bd03a" id="u4f4bd03a"><span data-lake-id="u56c39812" id="u56c39812">熔断器模式是防止微服务系统雪崩的一种重要手段。</span></p>
  <p data-lake-id="ub23bea74" id="ub23bea74"><br></p>
  <p data-lake-id="u5a2a433c" id="u5a2a433c"><span data-lake-id="u69589d6f" id="u69589d6f">一个比较完善的熔断器，一般包含三种状态：</span></p>
  <p data-lake-id="ue0af990c" id="ue0af990c"><br></p>
  <ul list="u988fbf2a">
   <li fid="uf5c41d53" data-lake-id="u34e7e1c5" id="u34e7e1c5"><span data-lake-id="u733579a0" id="u733579a0">关闭 </span></li>
  </ul>
  <ul list="u988fbf2a" data-lake-indent="1">
   <li fid="u77ccaa95" data-lake-id="u4c91c7a7" id="u4c91c7a7"><span data-lake-id="u4095e3ea" id="u4095e3ea">熔断器在默认情况下下是呈现关闭的状态，而熔断器本身带有计数功能，每当错误发生一次，计数器也就会进行“累加”的动作，到了一定的错误发生次数断路器就会被“开启”，这个时候亦会在内部启用一个计时器，一旦时间到了就会切换成半开启的状态。</span></li>
  </ul>
  <ul list="u988fbf2a" start="2">
   <li fid="uf5c41d53" data-lake-id="ue50c7149" id="ue50c7149"><span data-lake-id="ue6e88d79" id="ue6e88d79">开启 </span></li>
  </ul>
  <ul list="u988fbf2a" data-lake-indent="1">
   <li fid="uabd20235" data-lake-id="u949e5e63" id="u949e5e63"><span data-lake-id="u075f32ff" id="u075f32ff">在开启的状态下任何请求都会“直接”被拒绝并且抛出异常讯息。</span></li>
  </ul>
  <ul list="u988fbf2a" start="3">
   <li fid="uf5c41d53" data-lake-id="u7776c290" id="u7776c290"><span data-lake-id="u5d30d8af" id="u5d30d8af">半开启 </span></li>
  </ul>
  <ul list="u988fbf2a" data-lake-indent="1">
   <li fid="ua8eb87a2" data-lake-id="u65e7490a" id="u65e7490a"><span data-lake-id="ub1270eaa" id="ub1270eaa">在此状态下断路器会允许部分的请求，如果这些请求都能成功通过，那么就意味着错误已经不存在，则会被切换回关闭状态并重置计数。倘若请求中有“任一”的错误发生，则会恢复到“开启”状态，并且重新计时，给予系统一段休息时间。</span></li>
  </ul>
  <p data-lake-id="u25e2d783" id="u25e2d783"><br></p>
  <p data-lake-id="uaec30140" id="uaec30140"><img src="http://www.hollischuang.com/wp-content/uploads/2020/03/15842495525218.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_61%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="uf4368901" id="uf4368901">​</span></p>
  <p data-lake-id="u666ac735" id="u666ac735"><br></p>
  <p data-lake-id="uba7dffb1" id="uba7dffb1"><span data-lake-id="uf00df006" id="uf00df006">上图是熔断器的三种状态的转换情况。</span></p>
  <p data-lake-id="ud7df9279" id="ud7df9279"><br></p>
  <p data-lake-id="uc0402eba" id="uc0402eba"><span data-lake-id="u73c7c7a2" id="u73c7c7a2">如果在微服务系统的调用过程中，引入熔断器，那么整个系统将天然具备以下能力：</span></p>
  <p data-lake-id="u784c9fd7" id="u784c9fd7"><br></p>
  <ul list="u7611f825">
   <li fid="u5d76aab7" data-lake-id="u37d3df05" id="u37d3df05"><strong><span data-lake-id="uf261be98" id="uf261be98">快速失败</span></strong><span data-lake-id="ub487ae17" id="ub487ae17">：当因为调用远程服务失败次数过多，熔断器开启时，上游服务对于下游服务的调用就会快速失败，这样可以避免上游服务被拖垮。</span></li>
   <li fid="u5d76aab7" data-lake-id="ufba9ae8c" id="ufba9ae8c"><strong><span data-lake-id="u4a851106" id="u4a851106">无缝恢复</span></strong><span data-lake-id="ue41241ec" id="ue41241ec">：因为熔断器可以定期检查下游系统是否恢复，一旦恢复就可以重新回到关闭状态，所有请求便可以正常请求到下游服务。使得系统不需要人为干预。</span></li>
  </ul>
  <p data-lake-id="u98c9777c" id="u98c9777c"><br></p>
  <p data-lake-id="u71d57c96" id="u71d57c96"><span data-lake-id="u2e23a677" id="u2e23a677">Q：熔断模式我听懂了，那么这个模式如何运用到系统中啊？ A：其实熔断器的原理理解了，实现起来也不是很复杂 Q：那你们在代码中都是自己写熔断器的吗？ A：倒也不是，实现上有一些框架可以帮我们实现，并且还有些附加功能</span></p>
  <p data-lake-id="u4e80c29f" id="u4e80c29f"><br></p>
  <h3 data-lake-id="c3ec0aa0" id="c3ec0aa0"><span data-lake-id="uc4e7f888" id="uc4e7f888">熔断工具</span></h3>
  <p data-lake-id="u04b5a7f2" id="u04b5a7f2"><br></p>
  <p data-lake-id="ue6e4690b" id="ue6e4690b"><span data-lake-id="uef3b0068" id="uef3b0068">熔断器为了实现快速失败和无缝恢复，就需要进行服务调用次数统计、服务调用切断等操作，如果想要自己实现一个熔断器其实也是可以的。</span></p>
  <p data-lake-id="u6a6f56cf" id="u6a6f56cf"><br></p>
  <p data-lake-id="ue047a4df" id="ue047a4df"><span data-lake-id="u4bce0111" id="u4bce0111">但是，市面上有一些框架已经帮我们做了这些事情。如Hystrix和Sentinel、resilience4j等。</span></p>
  <p data-lake-id="u4122d52c" id="u4122d52c"><br></p>
  <h4 data-lake-id="Hystrix" id="Hystrix"><span data-lake-id="uaffa5e8e" id="uaffa5e8e">Hystrix</span></h4>
  <p data-lake-id="ub2091fcb" id="ub2091fcb"><br></p>
  <p data-lake-id="ua47ec2e6" id="ua47ec2e6"><span data-lake-id="u9628684f" id="u9628684f">Hystrix（</span><a href="https://github.com/Netflix/Hystrix" target="_blank" data-lake-id="ub6fb8707" id="ub6fb8707"><span data-lake-id="ub7eb7942" id="ub7eb7942">https://github.com/Netflix/Hystrix</span></a><span data-lake-id="ucdfc0cce" id="ucdfc0cce"> ）是Netflix开源的一款容错系统，能帮助使用者码出具备强大的容错能力和鲁棒性的程序。提供降级，熔断等功能。</span></p>
  <p data-lake-id="udc7b4f15" id="udc7b4f15"><br></p>
  <p data-lake-id="ua8e65d51" id="ua8e65d51"><span data-lake-id="uae9a9d3c" id="uae9a9d3c">但是，在2018年底，Hystrix在其Github主页宣布，不再开放新功能，推荐开发者使用其他仍然活跃的开源项目。</span></p>
  <p data-lake-id="u545d7339" id="u545d7339"><br></p>
  <p data-lake-id="uc4ea4e04" id="uc4ea4e04"><img src="http://www.hollischuang.com/wp-content/uploads/2020/03/15842512749500.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_32%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u7b70d2cd" id="u7b70d2cd">​</span></p>
  <p data-lake-id="u67ff6e66" id="u67ff6e66"><br></p>
  <p data-lake-id="u93b0e5b9" id="u93b0e5b9"><span data-lake-id="u6e175870" id="u6e175870">Hystrix虽然不再开发新功能 ，但对用户的影响应该不会太大，一是因为开发者可以继续使用Hystrix的最新版本1.5.18</span></p>
  <p data-lake-id="u9a41ea4b" id="u9a41ea4b"><br></p>
  <h4 data-lake-id="resilience4j" id="resilience4j"><span data-lake-id="u5a7d785a" id="u5a7d785a">resilience4j</span></h4>
  <p data-lake-id="u3441a207" id="u3441a207"><br></p>
  <p data-lake-id="u41146a78" id="u41146a78"><span data-lake-id="uaba2d574" id="uaba2d574">Hystrix停更之后，Netflix官方推荐使用resilience4j（</span><a href="https://github.com/resilience4j/resilience4j" target="_blank" data-lake-id="u0c61b243" id="u0c61b243"><span data-lake-id="u0703d9dc" id="u0703d9dc">https://github.com/resilience4j/resilience4j</span></a><span data-lake-id="ufae020dc" id="ufae020dc"> ），它是一个轻量、易用、可组装的高可用框架，支持熔断、高频控制、隔离、限流、限时、重试等多种高可用机制。</span></p>
  <p data-lake-id="ue751f856" id="ue751f856"><br></p>
  <p data-lake-id="u05a5ce58" id="u05a5ce58"><span data-lake-id="ud6791dd7" id="ud6791dd7">与Hystrix相比，它有以下一些主要的区别：</span></p>
  <p data-lake-id="uec2cec1c" id="uec2cec1c"><br></p>
  <ul list="u3b6b066a">
   <li fid="uee83343b" data-lake-id="u07581378" id="u07581378"><span data-lake-id="u3bfe79c9" id="u3bfe79c9">Hystrix调用必须被封装到HystrixCommand里，而resilience4j以装饰器的方式提供对函数式接口、lambda表达式等的嵌套装饰，因此你可以用简洁的方式组合多种高可用机制</span></li>
   <li fid="uee83343b" data-lake-id="u183585eb" id="u183585eb"><span data-lake-id="ua86c3d7e" id="ua86c3d7e">Hystrix的频次统计采用滑动窗口的方式，而resilience4j采用环状缓冲区的方式</span></li>
   <li fid="uee83343b" data-lake-id="uff07452c" id="uff07452c"><span data-lake-id="ub85050fc" id="ub85050fc">关于熔断器在半开状态时的状态转换，Hystrix仅使用一次执行判定是否进行状态转换，而resilience4j则采用可配置的执行次数与阈值，来决定是否进行状态转换，这种方式提高了熔断机制的稳定性</span></li>
   <li fid="uee83343b" data-lake-id="u37a00e71" id="u37a00e71"><span data-lake-id="ueac5737d" id="ueac5737d">关于隔离机制，Hystrix提供基于线程池和信号量的隔离，而resilience4j只提供基于信号量的隔离</span></li>
  </ul>
  <p data-lake-id="u6429c83a" id="u6429c83a"><br></p>
  <h4 data-lake-id="Sentinel" id="Sentinel"><span data-lake-id="ub3d1d22c" id="ub3d1d22c">Sentinel</span></h4>
  <p data-lake-id="u6b3da2fd" id="u6b3da2fd"><br></p>
  <p data-lake-id="ub30c9e27" id="ub30c9e27"><span data-lake-id="ue5515777" id="ue5515777">Sentinel（</span><a href="https://github.com/alibaba/Sentinel" target="_blank" data-lake-id="u1b167171" id="u1b167171"><span data-lake-id="udeedb590" id="udeedb590">https://github.com/alibaba/Sentinel</span></a><span data-lake-id="u39e6374f" id="u39e6374f"> ）是阿里中间件团队开源的，面向分布式服务架构的轻量级高可用流量控制组件，主要以流量为切入点，从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。</span></p>
  <p data-lake-id="u69bf7663" id="u69bf7663"><br></p>
  <p data-lake-id="u1fed9441" id="u1fed9441"><span data-lake-id="ua2ce184d" id="ua2ce184d">Hystrix 的关注点在于以隔离和熔断为主的容错机制，超时或被熔断的调用将会快速失败，并可以提供 fallback 机制。</span></p>
  <p data-lake-id="uf8caf2c3" id="uf8caf2c3"><br></p>
  <p data-lake-id="ufee2d642" id="ufee2d642"><span data-lake-id="u806fbe37" id="u806fbe37">而 Sentinel 的侧重点在于：</span></p>
  <p data-lake-id="u500ccc43" id="u500ccc43"><br></p>
  <ul list="ubf1de2d8">
   <li fid="u1c81c5ff" data-lake-id="u2c4aa1d4" id="u2c4aa1d4"><span data-lake-id="u9ec87b73" id="u9ec87b73">多样化的流量控制</span></li>
   <li fid="u1c81c5ff" data-lake-id="u35d58d63" id="u35d58d63"><span data-lake-id="u737ff421" id="u737ff421">熔断降级</span></li>
   <li fid="u1c81c5ff" data-lake-id="u54632424" id="u54632424"><span data-lake-id="ud7551923" id="ud7551923">系统负载保护</span></li>
   <li fid="u1c81c5ff" data-lake-id="ubc52647f" id="ubc52647f"><span data-lake-id="uf2222bd9" id="uf2222bd9">实时监控和控制台</span></li>
  </ul>
  <p data-lake-id="uf29cd017" id="uf29cd017"><br></p>
  <p data-lake-id="u7b671673" id="u7b671673"><span data-lake-id="u2e42dc03" id="u2e42dc03">下图是Sentinel的GitHub主页中关于Sentinel和Hystrix的对比：</span></p>
  <p data-lake-id="u5bf2ae9c" id="u5bf2ae9c"><br></p>
  <p data-lake-id="ufb405124" id="ufb405124"><img src="http://www.hollischuang.com/wp-content/uploads/2020/03/15842516892588.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_42%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u0eac4bcd" id="u0eac4bcd"><br></p>
  <p data-lake-id="u9d10a5c1" id="u9d10a5c1"><br></p>
 </body>
</html>